package org.ansj.library;

import org.ansj.dic.PathToStream;
import org.ansj.domain.KV;
import org.ansj.util.MyStaticValue;
import org.nlpcn.commons.lang.tire.domain.SmartForest;
import org.nlpcn.commons.lang.util.IOUtil;
import org.nlpcn.commons.lang.util.StringUtil;
import org.nlpcn.commons.lang.util.logging.Log;

import java.io.BufferedReader;
import java.util.*;
import java.util.Map.Entry;

public class SynonymsLibrary {

    private static final Log LOG = MyStaticValue.getLog(SynonymsLibrary.class);

    // 同义词典
    private static final Map<String, KV<String, SmartForest<List<String>>>> SYNONYMS = new HashMap<>();

    public static final String DEFAULT = "synonyms";

    static {
        for (Entry<String, String> entry : MyStaticValue.ENV.entrySet()) {
            if (entry.getKey().startsWith(DEFAULT)) {
                put(entry.getKey(), entry.getValue());
            }
        }
        putIfAbsent(DEFAULT, "library/synonyms.dic");
    }

    public static SmartForest<List<String>> get() {
        return get(DEFAULT);
    }

    /**
     */
    public static SmartForest<List<String>> get(String key) {
        KV<String, SmartForest<List<String>>> kv = SYNONYMS.get(key);

        if (kv == null) {
            if (MyStaticValue.ENV.containsKey(key)) {
                putIfAbsent(key, MyStaticValue.ENV.get(key));
                return get(key);
            }
            LOG.warn("crf " + key + " not found in config ");
            return null;
        }

        SmartForest<List<String>> sw = kv.getV();
        if (sw == null) {
            sw = init(key, kv, false);
        }
        return sw;
    }

    /**
     * 加载词典
     * 
     * @param key
     * @param kv
     * @param reload 是否更新词典
     * @return
     */
    private static synchronized SmartForest<List<String>> init(String key, KV<String, SmartForest<List<String>>> kv,
                    boolean reload) {

        SmartForest<List<String>> forest = kv.getV();

        if (forest != null) {
            if (reload) {
                forest.clear();
            } else {
                return forest;
            }
        } else {
            forest = new SmartForest<>();
        }

        LOG.debug("begin init synonyms " + kv.getK());
        long start = System.currentTimeMillis();

        try (BufferedReader reader = IOUtil.getReader(PathToStream.stream(kv.getK()), IOUtil.UTF8)) {
            String temp = null;
            while ((temp = reader.readLine()) != null) {
                if (StringUtil.isBlank(temp)) {
                    continue;
                }
                String[] split = temp.split("\t");

                List<String> list = new ArrayList<>();
                for (String word : split) {
                    if (StringUtil.isBlank(word)) {
                        continue;
                    }
                    list.add(word);
                }

                if (split.length <= 1) {
                    LOG.warn(temp + " in synonymsLibrary not in to library !");
                    continue;
                }

                for (int i = 0; i < split.length; i++) {
                    forest.add(split[i], list);
                }
            }
            kv.setV(forest);
            LOG.info("load synonyms use time:" + (System.currentTimeMillis() - start) + " path is : " + kv.getK());
            return forest;
        } catch (Exception e) {
            LOG.error("Init synonyms library error :" + e.getMessage() + ", path: " + kv.getK());
            SYNONYMS.remove(key);
            return null;
        }
    }

    /**
     * 动态添加
     * 
     * @param dicDefault
     * @param dicDefault2
     * @param dic2
     */
    public static void put(String key, String path) {
        put(key, path, null);
    }

    public static void put(String key, String path, SmartForest<List<String>> value) {
        SYNONYMS.put(key, KV.with(path, value));
        MyStaticValue.ENV.put(key, path);
    }

    /**
     * 删除一个key
     * 
     * @param key
     * @return
     */
    public static KV<String, SmartForest<List<String>>> remove(String key) {
        KV<String, SmartForest<List<String>>> kv = SYNONYMS.get(key);
        if (kv != null && kv.getV() != null) { //先清空后删除
            kv.getV().clear();
        }
        MyStaticValue.ENV.remove(key);
        return SYNONYMS.remove(key);
    }

    /**
     * 刷新一个,将值设置为null
     * 
     * @param key
     * @return
     */
    public static void reload(String key) {

        if (!MyStaticValue.ENV.containsKey(key)) { //如果变量中不存在直接删掉这个key不解释了
            remove(key);
        }

        putIfAbsent(key, MyStaticValue.ENV.get(key));

        KV<String, SmartForest<List<String>>> kv = SYNONYMS.get(key);

        init(key, kv, true);
    }

    public static Set<String> keys() {
        return SYNONYMS.keySet();
    }

    public static void putIfAbsent(String key, String path) {
        if (!SYNONYMS.containsKey(key)) {
            SYNONYMS.put(key, KV.with(path, (SmartForest<List<String>>) null));
        }
    }

    /**
     * 覆盖更新同义词 [中国, 中华, 我国] -> replace([中国,华夏]) -> [中国,华夏]
     * 
     * @param words
     */
    public static void insert(String key, String[] words) {
        SmartForest<List<String>> synonyms = get(key);

        List<String> list = new ArrayList<>();

        for (String word : words) {
            if (StringUtil.isBlank(word)) {
                continue;
            }
            list.add(word);
        }

        if (list.size() <= 1) {
            LOG.warn(Arrays.toString(words) + " not have any change because it less than 2 word");
            return;
        }

        Set<String> set = findAllWords(key, words);

        for (String word : list) {
            set.remove(word);
            synonyms.add(word, list);
        }

        for (String word : set) { //删除所有
            synonyms.remove(word);
            synonyms.getBranch(word).setParam(null);
        }

    }

    private static Set<String> findAllWords(String key, String[] words) {

        SmartForest<List<String>> synonyms = get(key);

        Set<String> set = new HashSet<>();
        for (String word : words) {
            SmartForest<List<String>> branch = synonyms.getBranch(word);
            if (branch != null) {
                List<String> params = branch.getParam();
                if (params != null) {
                    set.addAll(params);
                }
            }
        }
        return set;
    }

    /**
     * 合并更新同义词 覆盖更新同义词 [中国, 中华, 我国] -> append([中国,华夏]) -> [中国, 中华, 我国 , 华夏]
     * 
     * @param words
     */
    public static void append(String key, String[] words) {

        SmartForest<List<String>> synonyms = get(key);

        Set<String> set = new HashSet<>();

        for (String word : words) {
            if (StringUtil.isBlank(word)) {
                continue;
            }
            set.add(word);
        }

        if (set.size() <= 1) {
            LOG.warn(Arrays.toString(words) + " not have any change because it less than 2 word");
            return;
        }

        set.addAll(findAllWords(key, words));

        List<String> list = new ArrayList<>(set);

        for (String word : list) {
            synonyms.addBranch(word, list);
        }
    }

    /**
     * 从同义词组中删除掉一个词 [中国, 中华, 我国] -> remove(我国) -> [中国, 中华]
     * 
     * @param words
     */
    public static void remove(String key, String word) {

        SmartForest<List<String>> synonyms = get(key);

        SmartForest<List<String>> branch = synonyms.getBranch(word);

        if (branch == null || branch.getStatus() < 2) {
            return;
        }

        List<String> params = branch.getParam();

        synonyms.remove(word);
        branch.setParam(null);
        params.remove(word);

        if (params.size() == 1) { //如果是1 个也删除
            synonyms.remove(params.get(0));
            params.remove(0);
        } else {
            params.remove(word);
        }
    }
}
